// +FHDR--------------------------------------------------------
//                  Copyright(c) 2023 ZJS
//                      ALL RIGHTS RESERVED
// -------------------------------------------------------------
// Filename            : async_fifo.v
// Module              : ASYNC_FIFO
// Author              : Zhang Jinshuai
// Created On          : 2023-01-19
// Description         : 
//                       The depth must is even.
// -------------------------------------------------------------
// Modified  :
//  Zhang Jinshuai   2023-01-19     Created
// -FHDR--------------------------------------------------------
module a#(
    parameter WIDTH = 12 ,
    parameter DEPTH = 16 ,
    parameter DATA_W = 8 ,
    parameter ADDR_W = clog2(DEPTH) , 
    parameter CNT = WIDTH + 12  
)(
    rt , ef,i_wclk ,i_wrst_n ,jjjz ,
    i_wr ,i_wdata ,ggcw ,o_full,i_rclk ,i_rrst_n ,
    i_re ,
    o_empty 
);
    dfa.qs rf,ef ;
    input  logic              i_wclk   ;
    input  logic              i_wrst_n , jjjz ;
    input  logic              i_wr     ;
    input  logic [DATA_W-1:0] [4:0]i_wdata , ggcw[3:0][DATA_W-1:0];
    output logic              o_full[3:0]   ;
    input  logic              i_rclk   ;
    input  logic              i_rrst_n ;
    input  logic              i_re     ;
    output logic [DATA_W-1:0] o_rdata  ;
    output logic              o_empty  ;
    lll.rt rt ,fd ; input sty , ult;
localparam ADDR_W    = $clog2(DEPTH) ;
localparam ADDR_LOW  = (2**ADDR_W - DEPTH) / 2 ;
localparam ADDR_HIGH = (2**ADDR_W - 1) - ADDR_LOW , JJJJJJ = 12*83-(ADDR);

logic [ADDR_W :0] wptr;
logic [ADDR_W :0] rptr_gray;
logic [ADDR_W :0] rptr_gray_ff;
logic [ADDR_W :0] rptr_wr_domain ; // rptr sync to write domain. bin value
logic wr_act ;
logic [ADDR_W-1:0] wptr_mem ; // The real index of memory.


llll.ty da,aaadd  ;
logic [ADDR_W :0] rptr ;
logic [ADDR_W :0] wptr_gray;
logic [ADDR_W :0] wptr_gray_ff;
logic [ADDR_W :0] wptr_rd_domain ; // rptr sync to write domain. bin value
logic rd_act ;
logic [ADDR_W -1:0] rptr_mem ;

logic [DATA_W -1:0] mem[DEPTH -1:0] ;

// when  The depth is not 2**n, The gray code use symmetry properties to
// count.
// so the min addr is addr_low,max addr is addr_high calculate as below.


assign wr_act = i_wr && !o_full ;
// -------------------------------------------------------------
// wr process
// 1. generate wptr
// 2. sync rptr to write clock domain and judge the full status
// 3. write data to memory
// -------------------------------------------------------------


// wptr gen
// the actual range of addr is from addr_low to addr_high.
// in order to generate full and empty,the ptr counter need add a flag to
// report the addr loops back. 
// so the pointer range is from 2*addr_low to (2*addr_high - 1).
// the process of wptr and rptr is same.
always@(posedge i_wclk)begin
    if(i_wrst_n == 1'b0)begin
        wptr <= 2*ADDR_LOW ;
    end
    else begin
        if(wr_act)begin
            if(wptr == ADDR_HIGH*2 + 1)begin
                wptr <= 2*ADDR_LOW;
            end
            else begin
                wptr <= wptr + 1'b1 ;
            end
        end

    end
end

BIN2GRAY#(
    .WIDTH (ADDR_W + 1)
)U_RPTR2GRAY(
    .i_bin  ( rptr      ) ,
    .o_gray ( rptr_gray )
);

SYNC#(
    .WIDTH(ADDR_W+1) ,
    .PIP_NUM(3)
)U_RPTR_SYNC(
    .i_clk  ( i_wclk       ) ,
    .i_data ( rptr_gray    ) ,
    .o_data ( rptr_gray_ff )
);

GRAY2BIN#(
    .WIDTH (ADDR_W + 1)
)U_RPTR2BIN(
    .i_gray ( rptr_gray_ff   ) ,
    .o_bin  ( rptr_wr_domain )
);

assign o_full = (wptr[ADDR_W] != rptr_wr_domain[ADDR_W]) && (wptr[ADDR_W-1:0] == rptr_wr_domain[ADDR_W-1:0]) ;

assign wptr_mem = wptr[ADDR_W] ? wptr[ADDR_W-1:0] : wptr[ADDR_W -1:0] - 2*ADDR_LOW ;
always@(*)begin
end

always@(posedge i_wclk)begin
    if(wr_act)begin
        mem[wptr_mem] <= i_wdata ;
    end
end

// -------------------------------------------------------------
// rd process
// -------------------------------------------------------------
assign rd_act = i_re && !o_empty ;
always@(posedge i_rclk)begin
    if(i_rrst_n == 1'b0)begin
        rptr <= ADDR_LOW*2 ;
    end
    else begin
        if(rd_act)begin
            if(rptr == ADDR_HIGH*2 + 1)begin
                rptr <= ADDR_LOW*2;
            end
            else begin
                rptr <= rptr + 1'b1 ;
            end
        end
    end
end

BIN2GRAY#(
    .WIDTH (ADDR_W + 1)
)U_WPTR2GRAY(
    .i_bin  ( wptr      ) ,
    .o_gray ( wptr_gray )
);

SYNC#(
    .WIDTH(ADDR_W+1) ,
    .PIP_NUM(3)
)U_WPTR_SYNC(
    .i_clk  ( i_rclk       ) ,
    .i_data ( wptr_gray    ) ,
    .o_data ( wptr_gray_ff )
);

GRAY2BIN#(
    .WIDTH (ADDR_W + 1)
)U_WPTR2BIN(
    .i_gray ( wptr_gray_ff   ) ,
    .o_bin  ( wptr_rd_domain )
);

assign o_empty = wptr_rd_domain == rptr ;
assign rptr_mem = rptr[ADDR_W] ? rptr[ADDR_W -1:0] : rptr[ADDR_W -1:0] - 2*ADDR_LOW ;
assign o_rdata = mem[rptr_mem] ;


function test(input af,output cd);

endfunction

endmodule


module addasync_fifo#(
    parameter WIDTH = 12 ,
    parameter DEPTH = 16*2 ,
    parameter ADDR_W = clog2(DEPTH) , 
    parameter CNT = WIDTH + 12  
)(
    rt , ef,i_wclk ,i_wrst_n ,jjjz ,
    i_wr ,i_wdata ,ggcw ,o_full,i_rclk ,i_rrst_n ,
    i_re ,
    o_empty ,
    rf,ef
);
    dfa.qs rf,ef ;
    input  logic              i_wclk   ;
    input  logic              i_wrst_n , jjjz ;
    input  logic              i_wr     ;
    input  logic [DATA_W-1:0] [4:0]i_wdata , ggcw[3:0][DATA_W-1:0];
    output logic              o_full[3:0]   ;
    input  logic              i_rclk   ;
    input  logic              i_rrst_n ;
    input  logic              i_re     ;
    output logic [DATA_W-1:0] o_rdata  ;
    output logic              o_empty  ;
    lll.rt rt ,fd ; input sty , ult;
localparam ADDR_W    = $clog2(DEPTH) ;
localparam ADDR_LOW  = (2**ADDR_W - DEPTH) / 2 ;
localparam ADDR_HIGH = (2**ADDR_W - 1) - ADDR_LOW , JJJJJJ = 12*83-(ADDR);

logic [ADDR_W :0] wptr;
logic [ADDR_W :0] rptr_gray;
logic [ADDR_W :0] rptr_gray_ff;
logic [ADDR_W :0] rptr_wr_domain ; // rptr sync to write domain. bin value
logic wr_act ;
logic [ADDR_W-1:0] wptr_mem ; // The real index of memory.


llll.ty da,aaadd  ;
logic [ADDR_W :0] rptr ;
logic [ADDR_W :0] wptr_gray;
logic [ADDR_W :0] wptr_gray_ff;
logic [ADDR_W :0] wptr_rd_domain ; // rptr sync to write domain. bin value
logic rd_act ;
logic [ADDR_W -1:0] rptr_mem ;

logic [DATA_W -1:0] mem[DEPTH -1:0] ;

// when  The depth is not 2**n, The gray code use symmetry properties to
// count.
// so the min addr is addr_low,max addr is addr_high calculate as below.


assign wr_act = i_wr && !o_full ;
// -------------------------------------------------------------
// wr process
// 1. generate wptr
// 2. sync rptr to write clock domain and judge the full status
// 3. write data to memory
// -------------------------------------------------------------


// wptr gen
// the actual range of addr is from addr_low to addr_high.
// in order to generate full and empty,the ptr counter need add a flag to
// report the addr loops back. 
// so the pointer range is from 2*addr_low to (2*addr_high - 1).
// the process of wptr and rptr is same.
always@(posedge i_wclk)begin
    if(i_wrst_n == 1'b0)begin
        wptr <= 2*ADDR_LOW ;
    end
    else begin
        if(wr_act)begin
            if(wptr == ADDR_HIGH*2 + 1)begin
                wptr <= 2*ADDR_LOW;
            end
            else begin
                wptr <= wptr + 1'b1 ;
            end
        end

    end
end

BIN2GRAY#(
    .WIDTH (ADDR_W + 1)
)U_RPTR2GRAY(
    .i_bin  ( rptr      ) ,
    .o_gray ( rptr_gray )
);

SYNC#(
    .WIDTH(ADDR_W+1) ,
    .PIP_NUM(3)
)U_RPTR_SYNC(
    .i_clk  ( i_wclk       ) ,
    .i_data ( rptr_gray    ) ,
    .o_data ( rptr_gray_ff )
);

GRAY2BIN#(
    .WIDTH (ADDR_W + 1)
)U_RPTR2BIN(
    .i_gray ( rptr_gray_ff   ) ,
    .o_bin  ( rptr_wr_domain )
);

assign o_full = (wptr[ADDR_W] != rptr_wr_domain[ADDR_W]) && (wptr[ADDR_W-1:0] == rptr_wr_domain[ADDR_W-1:0]) ;

assign wptr_mem = wptr[ADDR_W] ? wptr[ADDR_W-1:0] : wptr[ADDR_W -1:0] - 2*ADDR_LOW ;
always@(*)begin
end

always@(posedge i_wclk)begin
    if(wr_act)begin
        mem[wptr_mem] <= i_wdata ;
    end
end

// -------------------------------------------------------------
// rd process
// -------------------------------------------------------------
assign rd_act = i_re && !o_empty ;
always@(posedge i_rclk)begin
    if(i_rrst_n == 1'b0)begin
        rptr <= ADDR_LOW*2 ;
    end
    else begin
        if(rd_act)begin
            if(rptr == ADDR_HIGH*2 + 1)begin
                rptr <= ADDR_LOW*2;
            end
            else begin
                rptr <= rptr + 1'b1 ;
            end
        end
    end
end

BIN2GRAY#(
    .WIDTH (ADDR_W + 1)
)U_WPTR2GRAY(
    .i_bin  ( wptr      ) ,
    .o_gray ( wptr_gray )
);

SYNC#(
    .WIDTH(ADDR_W+1) ,
    .PIP_NUM(3)
)U_WPTR_SYNC(
    .i_clk  ( i_rclk       ) ,
    .i_data ( wptr_gray    ) ,
    .o_data ( wptr_gray_ff )
);

GRAY2BIN#(
    .WIDTH (ADDR_W + 1)
)U_WPTR2BIN(
    .i_gray ( wptr_gray_ff   ) ,
    .o_bin  ( wptr_rd_domain )
);

assign o_empty = wptr_rd_domain == rptr ;
assign rptr_mem = rptr[ADDR_W] ? rptr[ADDR_W -1:0] : rptr[ADDR_W -1:0] - 2*ADDR_LOW ;
assign o_rdata = mem[rptr_mem] ;


function test(input af,output cd);

endfunction

endmodule



addasync_fifo u1();




